#include "table.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct table *table_new(struct string *table_name) {
  struct table *T = malloc(sizeof(*T));
  T->table_name = table_name;
  T->col_defs = NULL;
  T->col_count = 0;
  T->row_length = 0;
  //初始化读写锁
  pthread_rwlock_init(&T->rwlock_for_data, NULL);
  pthread_rwlock_init(&T->rwlock_for_tx, NULL);
  return T;
}

struct col_def *_table_col_def_new(struct string *col_name,
                                   enum col_type col_type, int length);

void _table_col_def_free(struct col_def *D);

void table_add_col_def(struct table *T, struct string *col_name,
                       enum col_type col_type, int length) {
  T->col_count++;
  if (!T->col_defs) {
    T->col_defs = malloc(sizeof(struct col_def *));
  } else {
    T->col_defs = realloc(T->col_defs, sizeof(struct col_def *) * T->col_count);
  }
  T->col_defs[T->col_count - 1] =
      _table_col_def_new(col_name, col_type, length);

  T->row_length += length;
}

// 根据列名查找这是第几列
int _table_find_col_def(struct table *T, struct string *col_name) {
  for (int i = 0; i < T->col_count; i++) {
    struct col_def *col_def = T->col_defs[i];
    if (string_equals(col_def->col_name, col_name)) {
      return i;
    }
  }
  return -1;
}

// 获取列在数据文件当中相对于本行第一列数据的偏移量
int _table_get_col_offset(struct table *T, int col_index) {
  int offset = 0;
  for (int i = 0; i < col_index; i++) {
    struct col_def *col_def = T->col_defs[i];
    offset += col_def->length;
  }
  return offset;
}

struct col_def *_table_col_def_new(struct string *col_name,
                                   enum col_type col_type, int length) {
  struct col_def *D = malloc(sizeof(*D));
  D->col_name = col_name;
  D->col_type = col_type;
  D->length = length;
  return D;
}

void _table_col_def_free(struct col_def *D) {
  string_free(D->col_name);
  free(D);
}

// 从数据文件中读取一个值，这个函数假定已经提前通过fseek定位到某一个位置了
struct string *_table_read_value_from_file(FILE *fp, int length) {
  char buff[length + 1];
  for (int i = 0; i < length; i++) {
    buff[i] = fgetc(fp);
  }
  int i = length - 1;
  for (; buff[i] == ' '; i--)
    ;
  buff[i + 1] = '\0';
  return string_new(buff);
}

// 判断某一行是否符合查询条件
bool _table_row_match_condition(struct table *T, struct condition **conditions,
                                int condition_count, FILE *fp, int row_index) {
  for (int j = 0; j < condition_count; j++) {
    struct condition *cond = conditions[j];
    int col_index = _table_find_col_def(T, cond->col_name);
    fseek(fp, (T->row_length + 10) * (long long)row_index +_table_get_col_offset(T, col_index),
          0);
    struct col_def *col_def = T->col_defs[col_index];
    struct string *value = _table_read_value_from_file(fp, col_def->length);
    if (!string_equals(cond->value, value)) {
      string_free(value);
      return false;
    }
    string_free(value);
  }
  return true;
}

bool _table_row_is_empty(struct table *T, FILE *fp, int row_index) {
  fseek(fp, (T->row_length + 10) * (long long)row_index, 0);
  char ch = fgetc(fp);
  if (ch == ' ') {
    for (int i = 1; i < T->row_length; i++) {
      ch = fgetc(fp);
      if (ch != ' ') {
        return false;
      }
    }
    return true;
  }
  return false;
}

struct col_name_value_pair *
table_col_name_value_pair_new(struct string *col_name,
                              struct string *col_value) {
  struct col_name_value_pair *pair = malloc(sizeof(*pair));
  pair->col_name = col_name;
  pair->col_value = col_value;
  return pair;
}

struct col_name_value_pair *
table_find_col_name_value_pair(struct col_name_value_pair **pairs,
                               int pair_count, struct string *col_name) {
  for (int i = 0; i < pair_count; i++) {
    struct col_name_value_pair *pair = pairs[i];
    if (string_equals(pair->col_name, col_name)) {
      return pair;
    }
  }
  return NULL;
}

void table_col_name_value_pair_free(struct col_name_value_pair *pair) {
  string_free(pair->col_name);
  string_free(pair->col_value);
  free(pair);
}

void table_free(struct table *T) {
  string_free(T->table_name);
  for (int i = 0; i < T->col_count; i++) {
    _table_col_def_free(T->col_defs[i]);
  }
  free(T->col_defs);
  pthread_rwlock_destroy(&T->rwlock_for_data);
  pthread_rwlock_destroy(&T->rwlock_for_tx);
  free(T);
}